package net.osc.addrpsr;

/**
 * User: ken
 * Date: 13-7-31
 */
import java.util.ArrayList;
import java.util.List;

import org.parboiled.BaseParser;
import org.parboiled.Rule;
import org.parboiled.annotations.BuildParseTree;

import net.osc.addrpsr.models.Address;


/**
 * User: ken
 * Date: 13-7-24
 */
@BuildParseTree
public class AddressParser extends BaseParser<Object> {

    /**抽取规则, 包含非地址串*/
    public Rule Extract() {
        return Sequence(
            ZeroOrMore(Sequence(TestNot(Address()), ANY)),
            Address()
        );
    }

    public Rule Address() {
        return Sequence(
            push(new Address()),
            Optional(Country()),
            Optional(Province()),
            FirstOf(
                Sequence(City(), District()),
                City(),
                District()
            ),
            Others()
        );
    }


    public Rule Country() {
        return Sequence(String("中国"), Optional(Sep()));
    }

    public Rule Province() {
        return Sequence(
            ProvinceName(),              push(popAsAddress().province(match())),
            Optional(ProvincePostfix()),
            Optional(Sep())
        );
    }

    public Rule ProvinceName() {
        return FirstOf(Regions.provinces());
    }

    public Rule ProvincePostfix() {
        return FirstOf(
            String("省"),
            Sequence(Optional(Races()), "自治区")
        );
    }

    public Rule City() {
        return Sequence(
            CityName(),                 push(popAsAddress().city(match())),
            Optional(CityPostfix()),
            Optional(Sep())
        );
    }

    public Rule CityName() {
        return FirstOf(Regions.cities());
    }

    public Rule CityPostfix() {
        return FirstOf(
            String("市"),
            String("地区"),
            Sequence(Optional(Races()), "自治州")
        );
    }

    /**todo: 增加语义限制, 比如厦门没有金山区*/
    public Rule District() {
        return Sequence(
            DistrictName(),            push(popAsAddress().district(match())),
            Optional(DistrictPostfix()),
            Optional(Sep())
        );
    }

    public Rule DistrictName() {
        return FirstOf(Regions.districts());
    }

    public Rule DistrictPostfix() {
        return FirstOf(
            String("县"),
            String("区"),
            Sequence(Optional(Races()), "自治县")
        );
    }


    public Rule Races() {
        return OneOrMore(FirstOf(Races.namesAndShortNames));
    }

    public Rule Others() {
        return FirstOf(
            Sequence(Road(), Building()),
            //Sequence(Building(), Road()),
            Road(),
            Building()
        );
    }

    public Rule Road() {
        return Sequence(
            Sequence(
                RoadName(),
                RoadPostfix()
            ),
            push(popAsAddress().road(match())),
            Optional(RoadNumber())
        );
    }

    public Rule RoadName() {
        return MinMaxTimes(1, 6, Sequence(TestNot(RoadPostfix()), ANY));
    }

    public Rule RoadPostfix() {
        return FirstOf("路", "大道", "道", "街", "巷", "胡同", "里", "镇", "厝", Sequence(TestNot("新"), "村"));
    }

    public Rule RoadNumber() {
        return Sequence(
            Digits(), push(popAsAddress().roadNo(match())),
            "号"
        );
    }

    //小区, 广场等
    public Rule Building() {
        return Sequence(
            Sequence(BuildingName(), BuildingPostfix()),       push(popAsAddress().building(match())),
            Optional(BuildingNumber()),                        push(popAsAddress().buildingNo(match()))
        );
    }

    public Rule BuildingNumber() {
        return MinMaxTimes(1, 10, Sequence(TestNot(FirstOf(Spaces(),AnyOf("（("), EOI)), ANY));
        //return MinMaxTimes(1, 10, Sequence(TestNot(Spaces()), ANY));
    }

    public Rule BuildingName() {
        return MinMaxTimes(1, 10, Sequence(TestNot(BuildingPostfix()), ANY));
    }

    public Rule BuildingPostfix() {
        return FirstOf("小区", "花园", "广场", "市场", "新城", "新村", "山庄", "公寓", "大厦", "饭店", "中心","大楼", "大楼", "酒店", "宾馆", "花园", "招待所", "基地", "寓", "苑", "园", "城", "庄");
    }

    public Rule Digits() {
        return OneOrMore(CharRange('0', '9'));
    }

    public Rule ChnDigit() {
        return AnyOf("一二三四五六七八九");
    }


    /*中文字符*/
    public Rule ChnChars() {
        return OneOrMore(CharRange('\u4E00', '\u9FA5'));
    }

    /*中文字符*/
    public Rule ChnChar() {
        return CharRange('\u4E00', '\u9FA5');
    }

    public Rule Sep() {
        return MinMaxTimes(1, 3, AnyOf(" 　.-―#•・·，（）()"));
    }

    public Rule Spaces(){
        return OneOrMore(AnyOf(" 　\t\f\r\n"));
    }

    public Rule MinMaxTimes(int min, int max, Rule rule) {
        List<Rule> rules = new ArrayList<Rule>();
        for (int i = max; i >= min; i--) {
            rules.add(NTimes(i, rule));
        }
        return FirstOf(rules.toArray(new Rule[rules.size()]));
    }

    public Address popAsAddress() {
        Address addr = getContext().getValueStack().size() == 0 ? new Address() : (Address) pop();
        return addr;
    }
}

